home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / DDJ0192.ARJ / SMARTDRV.ASC < prev    next >
Text File  |  1991-11-21  |  16KB  |  458 lines

  1. _UNTANGLING SMARTDRIVE_
  2. by Geoff Chappell
  3.  
  4. [LISTING ONE]
  5.  
  6. /*ioctl.c-functions to support IOCTL to & from devices under DOS all functions*
  7. * return 0 if successful, having stored an integer at an address provided as  *
  8. * the last argument failure is indicated by returning a non-0 DOS error code */
  9. unsigned _cdecl _dos_gethandlestatus (int handle, unsigned *status)
  10. {
  11.     _asm {
  12.             mov ax,4400h
  13.             mov bx,handle
  14.             int 21h
  15.             jc  done
  16.             mov bx,status
  17.             mov [bx],ax
  18.             xor ax,ax
  19.     done:
  20.     }
  21. }
  22. unsigned _cdecl _dos_ioctlread (int handle, void _far *buffer,
  23.     unsigned count, unsigned *numread)
  24. {
  25.     _asm {
  26.             push    ds
  27.             mov ax,4402h
  28.             mov bx,handle
  29.             mov cx,count
  30.             lds dx,buffer
  31.             int 21h
  32.             pop ds
  33.             jc  done
  34.             mov bx,numread
  35.             mov [bx],ax
  36.             xor ax,ax
  37.     done:
  38.     }
  39. }
  40. unsigned _cdecl _dos_ioctlwrite (int handle, void _far *buffer,
  41.     unsigned count, unsigned *numwrt)
  42. {
  43.     _asm {
  44.             push    ds
  45.             mov ax,4403h
  46.             mov bx,handle
  47.             mov cx,count
  48.             lds dx,buffer
  49.             int 21h
  50.             pop ds
  51.             jc  done
  52.             mov bx,numwrt
  53.             mov [bx],ax
  54.             xor ax,ax
  55.     done:
  56.     }
  57. }
  58.  
  59.  
  60.  
  61. [LISTING TWO]
  62.  
  63. /*** ioctl.h-function prototypes for IOCTL to & from devices under DOS ***/
  64. unsigned _dos_gethandlestatus (int, unsigned *);
  65. unsigned _dos_ioctlread (int, void _far *, unsigned, unsigned *);
  66. unsigned _dos_ioctlwrite (int, void _far *, unsigned, unsigned *);
  67.  
  68.  
  69.  
  70. [LISTING THREE]
  71.  
  72. /* smartdrv.h - structures and definitions relating to smartdrv.sys  */
  73. #pragma pack (1)
  74.  
  75. /*  The data packet returned by performing an IOCTL Read from SMARTDrive  */
  76. struct SD_READ {
  77.     char unused_1;
  78.     char unused_2;
  79.     char IsActive;
  80.     char MemoryType;
  81.     unsigned Ticks;
  82.     char IsLocked;
  83.     char FlushOnReboot;
  84.     char unused_3;
  85.     char DoubleBuffer;
  86.     void _far *OrgInt13;
  87.     char MinorVersion;
  88.     char MajorVersion;
  89.     char unused_4;
  90.     char unused_5;
  91.     unsigned SectorsRead;
  92.     unsigned SectorsHit;
  93.     unsigned SectorsBuffered;
  94.     char HitRatio;
  95.     char BufferRatio;
  96.     unsigned TracksInCache;
  97.     unsigned CurrentTracks;
  98.     unsigned LockedTracks;
  99.     unsigned DirtyTracks;
  100.     unsigned CurrentSize;
  101.     unsigned ConfiguredSize;
  102.     unsigned MinimumSize;
  103.     char _far *GlobalLockFlag;
  104. };
  105. struct SD_WRITE {
  106.     char command;
  107.     union {
  108.     char subcommand;
  109.     int size;
  110.     char _far *address;
  111.     };
  112. };
  113. #pragma pack ()
  114.  
  115.  
  116.  
  117. [LISTING FOUR]
  118.  
  119. /* smartchk.c-main source file for program smartchk.exe. Compile under small* 
  120.  * or tiny memory models in Microsoft C 6.00 and link with ioctl.obj        */
  121. #include    <bios.h>
  122. #include    <dos.h>
  123. #include    <fcntl.h>
  124. #include    <process.h>
  125. #include    <stdio.h>
  126. #include    <stdlib.h>
  127. #include    <string.h>
  128. #include    "ioctl.h"
  129. #include    "smartdrv.h"
  130. #define     AND     &&
  131. #define     NOT     !
  132. #define     OR      ||
  133.  
  134. /****  Function prototypes   ***/
  135. void get_configuration (void);
  136. void show_configuration (void);
  137. char ** get_range (int, char **);
  138. unsigned is_str_zero (char *);
  139. void format_command (char *, char **);
  140. void run_test (char *);
  141. int yes_or_no (void);
  142. int convert_to_seconds (long, long *, int *);
  143. void resize_cache (unsigned, unsigned);
  144. void reset_cache (void);
  145. void increment_cache (void);
  146.  
  147. void set_traps (void);
  148. void quit (char *);
  149. void cleanup (void);
  150. void _far CtrlC_trap (void);
  151. int _far critfail (void);
  152.  
  153. /***  Data ***/
  154. /*  The string describing the program's syntax  */
  155. const char syntax [] = "\
  156. \nGathers information about the SMARTDrive disk cache.\
  157. \n\
  158. \nSMARTCHK [/min [/max [/inc]]] command [arguments]\
  159. \n\
  160. \n    times the execution of the designated command,\
  161. \n    using different sizes for the SMARTDrive cache\
  162. \n\
  163. \n    min, max and inc should be multiples of 16KB\
  164. \n\
  165. \nType SMARTCHK without parameters to display information\
  166. \nabout SMARTDrive's configuration and performance.";
  167. /*  Structures for SMARTDrive IOCTL  */
  168. struct SD_READ sd_read;
  169. struct SD_WRITE sd_write;
  170. /*  Various pieces of data which must be shared between functions  */
  171. char terminating = 0;
  172. char sd_cache_changed = 0;
  173. unsigned sd_cache_size;
  174. int sd_handle = -1;
  175. unsigned min;
  176. unsigned max;
  177. unsigned inc;
  178.  
  179. /*** Code ***/
  180. int main (register int argc, register char *argv [])
  181. {
  182.     char buffer [128];
  183.   /* Install clean up routines and exception handlers to ensure termination. */
  184.     set_traps ();
  185.     /*  Verify that SMARTDrive has been installed and perform an IOCTL Read to 
  186.     obtain configuration data. Error in this operation is fatal to program.  */
  187.     get_configuration ();
  188.    /* If no argument has been supplied on the command line, then describe the 
  189.   configuration & report SMARTDrive's statistical estimates of its performance.
  190.   If the only argument on the command line is "/?", then display a help 
  191.   message. The most complicated option involves executing and timing a command 
  192.   repeatedly, using different sizes for the SMARTDrive cache. Up to 3 command 
  193.   line arguments may specify range of cache size to use in test-all arguments 
  194.   after these are presumed to be part of the command and are formatted
  195.   into a buffer for passing to the system () function.  */
  196.     argv ++;
  197.     argc --;
  198.     if (NOT argc) show_configuration ();
  199.     else if (argc == 1 AND **argv == '/' AND *(*argv + 1) == '?'
  200.             AND *(*argv + 2) == '\0') printf (syntax);
  201.     else {
  202.     argv = get_range (argc, argv);
  203.     format_command (buffer, argv);
  204.     run_test (buffer);
  205.     }
  206.     exit (0);
  207. }
  208. void get_configuration (void)
  209. {
  210.     register unsigned exitcode = 0;
  211.     unsigned status;
  212.     unsigned count;
  213.     /*  If smartdrv.sys has been loaded, it may be found in memory as a device 
  214.    driver with name SMARTAAR. Use _dos_open (), which is simply a front-end to 
  215.    the int 21h function 3Dh.  */
  216.     exitcode = _dos_open ("SMARTAAR", O_RDWR, &sd_handle);
  217.    /* On success, call int 21h function 4400h (not supported in MS-C library) 
  218.    to verify that the handle corresponds to a character device driver capable 
  219.    of IOCTL. DOS returns a 16-bit flag whose low byte it takes from the System 
  220.    File Table and high byte from the device driver attribute word. Status bit 
  221.    for a device is masked by 0x0080 and for IOCTL support by 0x4000 - both 
  222.    bits must be set.  */
  223.     if (NOT exitcode) {
  224.     exitcode = _dos_gethandlestatus (sd_handle, &status);
  225.     if (NOT exitcode) {
  226.         if ((status & 0x4080) != 0x4080) exitcode = 0xFFFF;
  227.     }
  228.     }
  229.     /*  Any error encountered so far can be explained by a common message. Note
  230.     that all errors occurring in this function are fatal to the program. */
  231.     if (exitcode) quit ("cannot open SMARTDrive device");
  232.    /* Perform IOCTL Read to get configuration info about SMARTDrive. Not only 
  233.    should read be successful but should return as many bytes as requested. */
  234.     exitcode = _dos_ioctlread (sd_handle, &sd_read, sizeof (sd_read), &count);
  235.     if (exitcode OR count != sizeof (sd_read))
  236.     quit ("cannot read data from SMARTDrive device");
  237. }
  238. void show_configuration (void)
  239. {
  240.     printf ("\nSMARTDrive Version %u.%02u has been configured to use %uKB",
  241.     sd_read.MajorVersion, sd_read.MinorVersion,
  242.     sd_read.ConfiguredSize << 4);
  243.     printf ("\nof %s memory with %uKB set as the minimum size.",
  244.     (sd_read.MemoryType == 1 ? "extended" : "expanded"),
  245.     sd_read.MinimumSize << 4);
  246.     printf ("\n\nIts present capacity is %uKB, corresponding to %u tracks.",
  247.     sd_read.CurrentSize << 4, sd_read.TracksInCache);
  248.     printf ("\nOf these, %u tracks are in use.", sd_read.CurrentTracks);
  249.     printf ("\n\nDuring this session, %u%% of sector reads have been filled",
  250.     sd_read.HitRatio);
  251.     printf ("\nfrom the cache and %u%% from the intermediate buffer.",
  252.     sd_read.BufferRatio);
  253. }
  254. char ** get_range (int argc, register char *argv [])
  255. {
  256.     register unsigned temp;
  257.     /*  Getting numerical arguments for the testing range is a little tedious, 
  258.     but unavoidable if program is to be anything other than a pointless toy.  
  259.     This function returns a value for the argv variable, advanced past any 
  260.     arguments that specify range. Begin by setting default values for range.
  261.     Change these only after establishing validity of command line value.  */
  262.     min = sd_read.MinimumSize;
  263.     max = sd_read.ConfiguredSize;
  264.     inc = 2;
  265.     if (**argv == '/') {
  266.     temp = atoi (*argv + 1) >> 4;
  267.     if (temp == 0 OR temp < min OR temp >= max) {
  268.         if (NOT is_str_zero (*argv + 1)) quit ("invalid minimum size");
  269.     }
  270.     min = temp;
  271.     argv ++;
  272.     argc --;
  273.     if (argc AND **argv == '/') {
  274.         temp = atoi (*argv + 1) >> 4;
  275.         if (temp <= min OR temp > max) quit ("invalid maximum size");
  276.         max = temp;
  277.         argv ++;
  278.         argc --;
  279.         if (argc AND **argv == '/') {
  280.         temp = atoi (*argv + 1);
  281.         if (NOT temp OR temp & 15 OR (temp >> 4 > max - min))
  282.             quit ("invalid increment");
  283.         inc = temp >> 4;
  284.         argv ++;
  285.         argc --;
  286.         if (min == 0 AND inc == 1)
  287.             quit ("parameters rejected to avoid SMARTDrive bug!");
  288.         }
  289.     }
  290.     }
  291.     /*  Name of a command to execute is mandatory. If no arguments remain, 
  292.     processing can't continue. Otherwise, return adjusted value for argv.  */
  293.     if (NOT argc) quit ("program name not supplied");
  294.     return (argv);
  295. }
  296. /*  The following simple function returns a TRUE value iff the string at
  297. address ptr is composed entirely of the character '0'.  */
  298. unsigned is_str_zero (register char *ptr)
  299. {
  300.     unsigned ch;
  301.     while ((ch = *ptr ++) AND (ch == '0')) {
  302.     }
  303.     return (ch ? 0 : 1);
  304. }
  305. /*  Given an array of pointers to character strings, the following function
  306. concatenates all strings, separating them with spaces and copying them to 
  307. specified buffer. Its role is to piece together a command string for system ()
  308. function. Note that it could be developed to strip double-quotes.  */
  309. void format_command (register char *buffer, char *argv [])
  310. {
  311.     register char *ptr;
  312.     while (ptr = *argv ++) {
  313.     while (*buffer ++ = *ptr ++) {
  314.     }
  315.     *(buffer - 1) = ' ';
  316.     }
  317.     *buffer = '\0';
  318. }
  319. void run_test (char *command_string)
  320. {
  321.     long start, end;
  322.     long seconds;
  323.     unsigned hundredths;
  324.     /*  Shrink the cache to the range's minimum.  */
  325.     resize_cache (min, sd_read.CurrentSize);
  326.     sd_cache_size = min;
  327.     sd_cache_changed = -1;
  328.     for (;;) {
  329.     /*  Before executing the command, flush the disk cache and discard its 
  330.        contents.  Each test is therefore started under the same conditions (at
  331.        least with respect to SMARTDrive, but note that test is not entirely 
  332.         fair, because data from disk is also held in DOS BUFFERS system).  */
  333.     reset_cache ();
  334.     /*  Use BIOS functions to get the system clock count, in spite of slight 
  335.     problem with the "midnight" flag. Calling MS-C library functions to obtain 
  336.     the time and convert the difference to seconds brings in floating point 
  337.     arithmetic & a great increase in code size if using an emulator library. */
  338.     _bios_timeofday (_TIME_GETCLOCK, &start);
  339.     system (command_string);
  340.     _bios_timeofday (_TIME_GETCLOCK, &end);
  341.     convert_to_seconds (end - start, &seconds, &hundredths);
  342.     /*  Report the cache size and execution time of the test program,
  343.     then give the user a chance to leave the testing cycle.  */
  344.     printf ("\n\nExecution time with %uKB disk cache was %lu.%02u seconds",
  345.         sd_cache_size << 4, seconds, hundredths);
  346.     printf ("\nContinue (y/n)?  ");
  347.     if (NOT yes_or_no ()) break;
  348.     printf ("\n");
  349.     /*  Increase the cache size for the next round of testing.  */
  350.     if (sd_cache_size + inc > max) break;
  351.     increment_cache ();
  352.     sd_cache_size += inc;
  353.     }
  354. }
  355. /*  The following is a simple function which waits at stdin, returning
  356. a true value on receipt of 'y' or a false value for 'n'.  */
  357. int yes_or_no (void)
  358. {
  359.     unsigned ch;
  360.     for (;;) {
  361.     ch = getch ();
  362.     if (NOT ch) getch ();
  363.     else if (ch == 'N' OR ch == 'n') return (0);
  364.     else if (ch == 'Y' OR ch == 'y') return (1);
  365.     }
  366. }
  367. convert_to_seconds (long clocks, long *seconds, int *hundredths)
  368. {
  369. /* Clock ticks at approximately 18.2/second, most easily rounded to 91 / 5.  */
  370.     *seconds = (clocks * 5) / 91;
  371.     *hundredths = (((clocks * 5) % 91) * 100) / 91;
  372. }
  373. /* === Cache manipulation ==== */
  374. /*  Next three functions perform IOCTL Write operations to change SMARTDrive 
  375. cache. In this particular program, error-reporting simplified by regarding all 
  376. errors as fatal.  */
  377. void resize_cache (unsigned new, unsigned old)
  378. {
  379.     unsigned exitcode, count;
  380.     sd_write.command = 0x0B;
  381.     sd_write.size = old - new;
  382.     if (sd_write.size == 0) return;
  383.     if (sd_write.size < 0) {
  384.     sd_write.size = - sd_write.size;
  385.     sd_write.command = 0x0C;
  386.     }
  387.     exitcode = _dos_ioctlwrite (sd_handle, &sd_write, 3, &count);
  388.     if (exitcode OR count != 3) quit ("cannot resize SMARTDrive cache");
  389. }
  390. void reset_cache (void)
  391. {
  392.     unsigned exitcode, count;
  393.     sd_write.command = 0x01;
  394.     exitcode = _dos_ioctlwrite (sd_handle, &sd_write, 1, &count);
  395.     if (exitcode OR count != 1) quit ("cannot flush SMARTDrive cache");
  396. }
  397. void increment_cache (void)
  398. {
  399.     unsigned exitcode, count;
  400.     sd_write.command = 0x0C;
  401.     sd_write.size = inc;
  402.     exitcode = _dos_ioctlwrite (sd_handle, &sd_write, 3, &count);
  403.     if (exitcode OR count != 3) quit ("cannot increase SMARTDrive cache");
  404. }
  405. /* === Code relating to termination - error messages and cleanup === */
  406. void set_traps (void)
  407. {
  408.    /*  Direct C run-time to call a cleanup routine before it exits program. */ 
  409.     atexit (cleanup);
  410.     /*  Trap Ctrl-C to ensure proper cleanup rather than let DOS terminate
  411.     program pre-emptively. Critical errors (which might also cause premature 
  412.     termination) may be failed automatically. An advantage to using _harderr ()
  413.     is that it allows compilation under tiny memory model. Were error handler 
  414.     installed directly, it would have to be declared using the _interrupt
  415.     keyword, which is (perhaps surprisingly) incompatible with tiny memory 
  416.     model. Even so, coercion to far addresses will generate unwanted segment 
  417.     references in the tiny memory model without some additional manipulation.*/
  418.     #define FAR_FUNCTION    (void (_far *)())
  419.     #define FAR_INTERRUPT   (void (_interrupt _far *)())
  420.     #define CODE_SEG        (void _based (_segname ("_CODE")) *)
  421.     _dos_setvect (0x23, FAR_INTERRUPT CODE_SEG CtrlC_trap);
  422.     _harderr (FAR_FUNCTION CODE_SEG critfail);
  423. }
  424. void cleanup (void)
  425. {
  426.     terminating = 1;
  427.     printf ("\n");
  428.     if (sd_handle != -1) {
  429.     if (sd_cache_changed) {
  430.         sd_cache_changed = 0;
  431.         reset_cache ();
  432.         resize_cache (sd_read.CurrentSize, sd_cache_size);
  433.     }
  434.     _dos_close (sd_handle);
  435.     sd_handle = -1;
  436.     }
  437. }
  438. void _far CtrlC_trap (void)
  439. {
  440.     if (NOT terminating) {
  441.     terminating ++;
  442.     quit ("terminated by user");
  443.     }
  444. }
  445. int _far critfail (void)
  446. {
  447.     return (_HARDERR_FAIL);
  448. }
  449. void quit (char *errmsg)
  450. {
  451.     printf ("\nUnable to continue - \n%s", errmsg);
  452.     exit (0xFF);
  453. }
  454.  
  455.  
  456.  
  457.  
  458.